<template>
  <div style="display: flex;overflow: hidden;height: 80vh;">
    <div class="scene-header-list" style="padding-top: 30px;">
      <div
        v-for="header in sceneHeaders"
        :key="header.id"
        class="scene-header-item"
        @click="goToSceneHeader(header.id)"
      >
        {{ header.episode }}集 {{ header.scene }}场 ｜ {{ header.sceneName }} {{ header.mood }}｜{{ header.interiorExteriorScene }}
      </div>
    </div>
    <div class="multi-page-editor" style="overflow: auto;height: 100%;">
      <div style="margin-top: 12px;position: fixed;top: 0;">
        <button @click="insertSceneHeader">插入场头</button>
        <button @click="undo">撤销</button>
        <button @click="redo">重做</button>
        <button @click="copySelection">复制</button>
        <button @click="cutSelection">剪切</button>
        <button @click="pasteFromClipboard">粘贴</button>
        <button @click="markAsCharacter">角色</button>
        <button @click="openFindReplaceDialog">查找替换</button>
      </div>
      <div
        v-for="(page, idx) in pages"
        :key="idx"
        class="page"
      >
        <EditorContent
          :editor="page.editor"
          class="page-editor"
          @keydown="handleKeyDown(idx, $event)"
        />
        <div class="page-number">第 {{ idx + 1 }} 页</div>
      </div>
    </div>
       <!-- 查找替换弹窗 -->
       <div v-if="showFindReplaceDialog" class="find-replace-dialog">
      <div class="dialog-header">
        <h3>查找与替换</h3>
        <button @click="closeFindReplaceDialog" class="close-btn">×</button>
      </div>
      <div class="dialog-body">
        <div class="input-group">
          <label for="find-input">查找内容:</label>
          <input 
            id="find-input"
            v-model="findText" 
            @keyup.enter="findNext"
            placeholder="输入要查找的内容"
          />
        </div>
        <div class="input-group">
          <label for="replace-input">替换为:</label>
          <input 
            id="replace-input"
            v-model="replaceText" 
            @keyup.enter="replaceNext"
            placeholder="输入替换内容"
          />
        </div>
        
        <div class="match-info">
          找到 {{ matchCount }} 处匹配
          <span v-if="currentMatchIndex > -1">
            ({{ currentMatchIndex + 1 }}/{{ matchCount }})
          </span>
        </div>
        
        <div class="dialog-actions">
          <button @click="findPrev">上一个</button>
          <button @click="findNext">下一个</button>
          <button @click="replaceCurrent">替换</button>
          <button @click="replaceAll">全部替换</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, nextTick, onMounted,watch } from "vue";
import { Editor, EditorContent } from "@tiptap/vue-3";
import { TextSelection } from 'prosemirror-state';
import StarterKit from "@tiptap/starter-kit";
import TextStyle from "@tiptap/extension-text-style";
import Underline from '@tiptap/extension-underline'
import FontFamily from '@tiptap/extension-font-family'
import { 
  SceneHeaderNode,
  TextTypeMark,
  CustomParagraph,
} from './utils'

const PAGE_HEIGHT = 1122 // 根据你实际设置（A4 页面高度

const pages = ref([]);
let activePageIndex = 0;
let syncing = false;
const undoStack = [];
const redoStack = [];
const isComposing = ref(false);

// 创建 editor 实例
function createEditor(content, pageIndex) {
  const editor = new Editor({
    extensions: [
      StarterKit.configure({
        paragraph: false, // ❗禁用默认 paragraph
      }),
      TextStyle,
      FontFamily,
      Underline,
      SceneHeaderNode,
      CustomParagraph,     // ✅ 使用带 class 的段落扩展,
      TextTypeMark, // 👈 注册它
    ],
    content,
    onTransaction({ editor, transaction }) {
      if (!isComposing.value && transaction.docChanged) {
        syncContent()
      }
    },
    onFocus() {
      activePageIndex = pageIndex;
      scrollPageIntoCenter(pageIndex);
    },
  });

  nextTick(() => {
    const editorEl = editor.view.dom;
    if (!editorEl._compositionListenersAdded) {
      editorEl.addEventListener("compositionstart", () => {
        isComposing.value = true;
      });
      editorEl.addEventListener("compositionend", () => {
        isComposing.value = false;
        syncContent();
      });
      editorEl._compositionListenersAdded = true;
    }
  });

  return editor;
}

// 按实际内容高度分页
async function splitByHeight(html) {
  return new Promise((resolve) => {
    requestAnimationFrame(() => {
      const tempContainer = document.createElement("div");
      tempContainer.style.position = "absolute";
      tempContainer.style.visibility = "hidden";
      tempContainer.style.width = "794px";
      tempContainer.style.padding = "48px 72px 64px";
      tempContainer.style.boxSizing = "border-box";
      tempContainer.innerHTML = html;
      document.body.appendChild(tempContainer);

      const chunks = [];
      let currentChunk = document.createElement("div");

      const nodes = Array.from(tempContainer.childNodes);
      tempContainer.innerHTML = "";

      for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        currentChunk.appendChild(node.cloneNode(true));

        tempContainer.innerHTML = "";
        tempContainer.appendChild(currentChunk.cloneNode(true));

        if (tempContainer.scrollHeight > PAGE_HEIGHT) {
          currentChunk.removeChild(currentChunk.lastChild);
          chunks.push(currentChunk.innerHTML);
          currentChunk = document.createElement("div");
          currentChunk.appendChild(node.cloneNode(true));
        }
      }

      if (currentChunk.childNodes.length > 0) {
        chunks.push(currentChunk.innerHTML);
      }

      document.body.removeChild(tempContainer);
      resolve(chunks);
    });
  });
}


// 撤销快照
function pushUndoSnapshot() {
  const html = pages.value.map(p => p.editor.getHTML()).join("");
  const editor = pages.value[activePageIndex]?.editor;
  const offset = editor?.state.selection.from ?? 0;

  undoStack.push({
    html,
    cursor: { pageIndex: activePageIndex, offset },
  });

  if (undoStack.length > 50) undoStack.shift();
  redoStack.length = 0;
}

function scrollPageIntoCenter(pageIndex) {
  nextTick(() => {
    const pageEl = document.querySelectorAll(".page")[pageIndex];
    if (pageEl) {
      pageEl.scrollIntoView({ behavior: "smooth", block: "nearest" });
    }
  });
}

// 场头
const sceneHeaders = ref([]);

// 初始化页面
async function initPages(content) {
  const chunks = await splitByHeight(content);
  pages.value = chunks.map((chunk, idx) => ({
    content: chunk,
    editor: createEditor(chunk, idx),
  }));
  pushUndoSnapshot();
  scrollPageIntoCenter(0);
  refreshSceneHeaderList()
}

// 同步并分页
async function syncContent() {
  
  if (syncing) return;
  syncing = true;

  pushUndoSnapshot();

  const activeEditor = pages.value[activePageIndex]?.editor;
  const selection = activeEditor?.state.selection;

  let globalAnchor = 0;
  let globalHead = 0;

  if (selection) {
    globalAnchor = computeGlobalPos(selection.anchor, activeEditor);
    globalHead = computeGlobalPos(selection.head, activeEditor);
  }

  const fullHTML = preprocessHTML(pages.value.map(p => p.editor.getHTML()).join(""));
  const newChunks = await splitByHeight(fullHTML);
  const currentChunks = pages.value.map(p => preprocessHTML(p.editor.getHTML()));

  const newPages = [];

  for (let i = 0; i < newChunks.length; i++) {
    const chunk = newChunks[i];
    const currentChunk = currentChunks[i];

    if (chunk === currentChunk && pages.value[i]) {
      newPages.push(pages.value[i]);
    } else {
      if (pages.value[i]) pages.value[i].editor.destroy();
      newPages.push({ content: chunk, editor: createEditor(chunk, i) });
    }
  }

  for (let i = newChunks.length; i < pages.value.length; i++) {
    pages.value[i]?.editor?.destroy?.();
  }

  pages.value = newPages;
  await nextTick();

  let cumLength = 0;
  for (let i = 0; i < pages.value.length; i++) {
    const editor = pages.value[i].editor;
    const html = editor.getHTML();
    const div = document.createElement("div");
    div.innerHTML = html;
    const textLen = (div.textContent || "").length;
    const nextCum = cumLength + textLen;

    if (globalAnchor <= nextCum) {
      const localAnchor = Math.max(0, globalAnchor - cumLength);
      const localHead = Math.max(0, globalHead - cumLength);

      const state = editor.state;
      const sel = TextSelection.create(state.doc, localAnchor, localHead);
      const tr = state.tr.setSelection(sel).scrollIntoView();
      editor.view.dispatch(tr);

      editor.commands.focus();
      activePageIndex = i;
      scrollPageIntoCenter(i);
      break;
    }

    cumLength = nextCum;
  }

  syncing = false;
}

// 撤销
function undo() {
  if (!undoStack.length) return;
  const last = undoStack.pop();
  const currentHtml = pages.value.map(p => p.editor.getHTML()).join("");
  const offset = pages.value[activePageIndex]?.editor.state.selection.from ?? 0;

  redoStack.push({ html: currentHtml, cursor: { pageIndex: activePageIndex, offset } });
  restoreFromSnapshot(last);
}

// 重做
function redo() {
  if (!redoStack.length) return;
  const next = redoStack.pop();
  const currentHtml = pages.value.map(p => p.editor.getHTML()).join("");
  const offset = pages.value[activePageIndex]?.editor.state.selection.from ?? 0;

  undoStack.push({ html: currentHtml, cursor: { pageIndex: activePageIndex, offset } });
  restoreFromSnapshot(next);
}

// 复制
function copySelection() {
  const editor = pages.value[activePageIndex]?.editor
  if (!editor) return;
  const selectedText = editor.state.doc.textBetween(
    editor.state.selection.from,
    editor.state.selection.to,
    "\n"
  );
  if (selectedText) {
    navigator.clipboard.writeText(selectedText);
  }
}

// 剪切
function cutSelection() {
  const editor = pages.value[activePageIndex]?.editor
  if (!editor) return;
  const { state, commands } = editor;
  const selectedText = state.doc.textBetween(
    state.selection.from,
    state.selection.to,
    "\n"
  );
  if (selectedText) {
    navigator.clipboard.writeText(selectedText);
    commands.deleteSelection(); // 删除选中文本
    syncContent();
  }
}

// 粘贴
async function pasteFromClipboard() {
  const editor = pages.value[activePageIndex]?.editor
  if (!editor) return;
  const text = await navigator.clipboard.readText();
  if (text) {
    editor.commands.insertContent(text);
    syncContent();
  }
}


// 快照恢复
async function restoreFromSnapshot(snapshot) {
  const { html, cursor } = snapshot;
  pages.value.forEach((p) => p.editor.destroy());
  const paseHTML = preprocessHTML(html)
  const chunks = await splitByHeight(paseHTML);
  pages.value = chunks.map((chunk, idx) => ({
    content: chunk,
    editor: createEditor(chunk, idx),
  }));

  await nextTick();

  const page = pages.value[cursor.pageIndex];
  if (page) {
    const offset = Math.min(cursor.offset, page.editor.state.doc.content.size);
    page.editor.commands.focus(offset);
    activePageIndex = cursor.pageIndex;
    scrollPageIntoCenter(cursor.pageIndex);
  }
}

// 光标跨页处理
function handleKeyDown(idx, event) {
  const editor = pages.value[idx]?.editor;
  if (!editor) return;

  const { state } = editor;
  const { selection } = state;
  const { $from } = selection;

  // 🧱 阻止删除紧跟场头节点
  if (event.key === "Backspace" && selection.empty) {
    const pos = $from.pos;
    if (pos === 0) {
      
      // 光标已在文档最开始，无法再删除，直接返回
      event.preventDefault();
      return;
    }

    const docSize = state.doc.content.size;

    // 如果删除最后一个字符后文档为空，插入空段落
    if (docSize === 1) {
      
      event.preventDefault();

      editor
        .chain()
        .focus()
        .setContent('') // 清空内容，TipTap 里这会插入空段落
        .run();

      return;
    }
    // 当前节点位置是块级的开头，检查前一个节点
    const posBefore = $from.before($from.depth);
    const nodeBefore = state.doc.resolve(posBefore).nodeBefore;

    // 判断当前段是否为空
    const isCurrentBlockEmpty = $from.parent.isTextblock && $from.parent.content.size === 1;
    
    if(isCurrentBlockEmpty && nodeBefore?.type?.name === 'sceneHeader') {
      event.preventDefault();
      return;
    }
  }

  // ⏩ 跨页导航逻辑保持
  if (
    event.key === "ArrowRight" &&
    selection.$to.pos === editor.state.doc.content.size &&
    idx < pages.value.length - 1
  ) {
    event.preventDefault();
    pages.value[idx + 1].editor.commands.focus("start");
  } else if (
    event.key === "ArrowLeft" &&
    selection.$from.pos === 0 &&
    idx > 0
  ) {
    event.preventDefault();
    const lastPos = pages.value[idx - 1].editor.state.doc.content.size;
    pages.value[idx - 1].editor.commands.focus(lastPos);
  }
}


function insertSceneHeader() {
  const editor = pages.value[activePageIndex]?.editor
  if (editor) {
    editor.commands.insertSceneHeaderWithFocus()
    setTimeout(() => {
      syncContent()
    }, 0)
  }
}

// 设置角色
function markAsCharacter() {
  const editor = pages.value[activePageIndex]?.editor
  if (!editor) return

  const { from, to } = editor.state.selection
  const selectedText = editor.state.doc.textBetween(from, to, ' ').trim()

  if (!selectedText) return

  // 作为独立段落式的配置
  const paragraphOption = {
      type: 'paragraph',
      attrs: { class: 'character-line' }, // ✅ 居中段落样式
      content: [
        {
          type: 'text',
          text: selectedText,
          marks: [
            {
              type: "textType",
              attrs: {
                text_type: "CHARACTER_NAME"
              }
            }
          ]
        }
      ]
    }

    // 行内样式配置
    const textOption = {
      type: 'text',
      text: selectedText,
      marks: [
        {
          type: "textType",
          attrs: {
            text_type: "CHARACTER_NAME",
            data_style:'font-weight:bold'
          }
        }
      ],
    }

  editor.chain()
    .focus()
    .deleteSelection()
    .insertContent(textOption)
    .run()

  setTimeout(() => {
    syncContent()
  }, 0)
}




function computeGlobalPos(localPos, editorInstance) {
  let offset = 0;
  for (let i = 0; i < pages.value.length; i++) {
    const editor = pages.value[i].editor;
    if (editor === editorInstance) {
      return offset + localPos;
    }
    const html = editor.getHTML();
    const div = document.createElement("div");
    div.innerHTML = html;
    offset += (div.textContent || "").length;
  }
  return offset;
}

async function deleteSceneHeaderGlobal(globalPos) {
  const fullHTML = pages.value.map(p => p.editor.getHTML()).join("");
  const paseFullHTML = preprocessHTML(fullHTML)

  const tempEditor = new Editor({
    extensions: [
      StarterKit.configure({
        paragraph: false, // ❗禁用默认 paragraph
      }),
      TextStyle,
      FontFamily,
      Underline,
      SceneHeaderNode,
      CustomParagraph,     // ✅ 使用带 class 的段落扩展,
      TextTypeMark, // 👈 注册它
    ],
    content:paseFullHTML,
  });

  const { doc, tr } = tempEditor.state;

  let startPos = globalPos;
  let endPos = doc.content.size;

  doc.descendants((node, posHere) => {
    if (posHere > globalPos && node.type.name === 'sceneHeader') {
      endPos = posHere;
      return false;
    }
    return true;
  });

  tr.delete(startPos, endPos);
  tempEditor.view.dispatch(tr);

  const paseHTML = preprocessHTML(tempEditor.getHTML())

  const newChunks = await splitByHeight(paseHTML);

  pages.value.forEach((p) => p.editor.destroy());
  pages.value = newChunks.map((chunk, idx) => ({
    content: chunk,
    editor: createEditor(chunk, idx),
  }));

  await nextTick();

  scrollPageIntoCenter(0);
  pushUndoSnapshot();
}


function preprocessHTML(html) {
  const div = document.createElement('div')
  div.innerHTML = html

  const spans = div.querySelectorAll('span[text_type]')
  spans.forEach(span => {
    
    const textType = span.getAttribute('text_type')
    const style = span.getAttribute('style')
    const innerText = span.textContent
    

    // 用 tiptap mark 语法包装
    const newSpan = document.createElement('span')
    newSpan.setAttribute('data-type', 'textStyle')
    newSpan.setAttribute('text_type', textType)
    newSpan.setAttribute('data_style', style || '')
    newSpan.textContent = innerText
    

    span.replaceWith(newSpan)
  })

  return div.innerHTML
}

const styles = {
  "CHARACTER_NAME": {
    "font-size": "18pt",
    "font-family": "'华文楷体', STKaiti, serif",
    "font-weight": "bold",
    "font-style": "italic",
    "text-decoration": "underline",
    "letter-spacing": "12pt"
  },
  "DIALOGUE": {
    "font-size": "12pt",
    "font-family": "'宋体', SimSun",
    "letter-spacing": "12pt"
  },
  "DIALOGUE_DESCRIPTION": {
    "font-size": "12pt",
    "font-family": "'黑体', SimHei",
    "letter-spacing": "20pt",
    "text-align": "center"
  }
}

// 将样式应用到全局
function applyTextTypeStylesFromServer(styleConfig) {
  const styleEl = document.createElement('style')
  styleEl.setAttribute('id', 'dynamic-text-type-style')

  const css = Object.entries(styleConfig).map(([type, styles]) => {
    const cssRules = Object.entries(styles)
      .map(([key, val]) => `${key}: ${val};`)
      .join(' ')
    return `span[text_type="${type}"] { ${cssRules} }`
  }).join('\n')

  styleEl.textContent = css
  document.head.appendChild(styleEl)
}

function getAllSceneHeaders() {
  const sceneHeaders = [];
  let globalOffset = 0;

  pages.value.forEach((page, pageIndex) => {
    const { doc } = page.editor.state;

    doc.descendants((node, pos) => {
      if (node.type.name === 'sceneHeader') {
        sceneHeaders.push({
          id: node.attrs.id,
          episode: node.attrs.episode,
          scene: node.attrs.scene,
          sceneName: node.attrs.sceneName,
          mood: node.attrs.mood,
          interiorExteriorScene: node.attrs.interiorExteriorScene,
          globalPos: globalOffset + pos,
          pageIndex,
        });
      }
    });

    const html = page.editor.getHTML();
    const div = document.createElement("div");
    div.innerHTML = html;
    globalOffset += (div.textContent || "").length;
  });
  console.log(sceneHeaders,'sceneHeaders');
  
  return sceneHeaders;
}
// 滚动到目标场头
function scrollToSceneHeaderById(sceneId) {
  const pageEls = document.querySelectorAll('.page');
  for (let i = 0; i < pageEls.length; i++) {
    const target = pageEls[i].querySelector(`div[id="${sceneId}"]`);
    if (target) {
      target.scrollIntoView({ behavior: 'smooth', block: 'center' });
      break;
    }
  }
}

function refreshSceneHeaderList() {
  sceneHeaders.value = getAllSceneHeaders();
}

function goToSceneHeader(sceneId) {
  scrollToSceneHeaderById(sceneId);
}

// 新增查找替换相关状态
const showFindReplaceDialog = ref(false);
const findText = ref('');
const replaceText = ref('');
const matchCount = ref(0);
const currentMatchIndex = ref(-1);
const currentMatches = ref([]); // 存储匹配项的位置信息

// 打开查找替换弹窗
function openFindReplaceDialog() {
  showFindReplaceDialog.value = true;
  nextTick(() => {
    document.getElementById('find-input')?.focus();
  });
}

// 关闭查找替换弹窗
function closeFindReplaceDialog() {
  showFindReplaceDialog.value = false;
  findText.value = '';
  replaceText.value = '';
  matchCount.value = 0;
  currentMatchIndex.value = -1;
  currentMatches.value = [];
}

// 在文档中查找匹配项
function findAllMatches() {
  currentMatches.value = [];
  matchCount.value = 0;
  currentMatchIndex.value = -1;
  
  if (!findText.value.trim()) return;
  
  const searchText = findText.value.toLowerCase();
  
  // 遍历所有页面查找匹配项
  pages.value.forEach((page, pageIndex) => {
    const editor = page.editor;
    const { state } = editor;
    const { doc } = state;
    
    let pos = 0;
    doc.descendants((node, nodePos) => {
      if (node.isText && node.marks) {
        // 检查是否有CHARACTER_NAME标记
        const hasCharacterNameMark = node.marks.some(mark => 
          mark.type.name === 'textType' && 
          mark.attrs.text_type === 'CHARACTER_NAME'
        );
        
        if (hasCharacterNameMark) {
          const text = node.text.toLowerCase();
          let index = text.indexOf(searchText);
          
          while (index !== -1) {
            const from = nodePos + index;
            const to = from + searchText.length;
            
            currentMatches.value.push({
              pageIndex,
              from,
              to,
              node,
              nodePos
            });
            
            index = text.indexOf(searchText, index + 1);
          }
        }
      }
      return true;
    });
  });
  
  matchCount.value = currentMatches.value.length;
  
  if (matchCount.value > 0) {
    currentMatchIndex.value = 0;
    selectMatch(currentMatches.value[0]);
  }
}

// 选中匹配项
function selectMatch(match) {
  const { pageIndex, from, to } = match;
  const editor = pages.value[pageIndex]?.editor;
  
  if (editor) {
    // 激活页面
    activePageIndex = pageIndex;
    // 滚动到该页
    scrollPageIntoCenter(pageIndex);
    
    // 设置选中范围
    setTimeout(() => {
      editor.commands.setTextSelection({ from, to });
      editor.commands.focus();
    }, 100);
  }
}

// 查找下一个匹配项
function findNext() {
  if (!findText.value.trim()) return;
  
  if (currentMatches.value.length === 0) {
    findAllMatches();
    return;
  }
  
  if (currentMatchIndex.value < currentMatches.value.length - 1) {
    currentMatchIndex.value++;
  } else {
    currentMatchIndex.value = 0; // 循环到第一个
  }
  
  selectMatch(currentMatches.value[currentMatchIndex.value]);
}

// 查找上一个匹配项
function findPrev() {
  if (!findText.value.trim()) return;
  
  if (currentMatches.value.length === 0) {
    findAllMatches();
    return;
  }
  
  if (currentMatchIndex.value > 0) {
    currentMatchIndex.value--;
  } else {
    currentMatchIndex.value = currentMatches.value.length - 1; // 循环到最后一个
  }
  
  selectMatch(currentMatches.value[currentMatchIndex.value]);
}

// 替换当前选中的匹配项
function replaceCurrent() {
  if (currentMatchIndex.value === -1 || !replaceText.value) return;
  
  const match = currentMatches.value[currentMatchIndex.value];
  const editor = pages.value[match.pageIndex]?.editor;
  
  if (editor) {
    const { state } = editor;
    const { from, to, node } = match;
    
    // 获取当前文本的样式标记
    const marks = state.doc.resolve(from).marks();
    
    // 只保留CHARACTER_NAME标记
    const characterNameMarks = marks.filter(mark => 
      mark.type.name === 'textType' && 
      mark.attrs.text_type === 'CHARACTER_NAME'
    );
    
    // 执行替换
    editor.chain()
      .focus()
      .setTextSelection({ from, to })
      .insertContent({
        type: 'text',
        text: replaceText.value,
        marks: characterNameMarks
      })
      .run();
    
    // 更新匹配项列表
    setTimeout(() => {
      findAllMatches();
      if (currentMatches.value.length === 0) {
        currentMatchIndex.value = -1;
      } else {
        // 保持当前索引位置（如果可能）
        currentMatchIndex.value = Math.min(currentMatchIndex.value, currentMatches.value.length - 1);
      }
    }, 50);
  }
}

// 替换所有匹配项
function replaceAll() {
  if (matchCount.value === 0 || !replaceText.value) return;
  
  // 保存当前匹配索引
  const currentIndex = currentMatchIndex.value;
  
  // 替换所有匹配项
  pages.value.forEach((page, pageIndex) => {
    const editor = page.editor;
    const { state } = editor;
    const { tr } = state;
    
    // 获取该页的所有匹配项
    const pageMatches = currentMatches.value
      .filter(m => m.pageIndex === pageIndex)
      .sort((a, b) => b.from - a.from); // 从后往前替换，避免位置变化
    
    let modified = false;
    
    pageMatches.forEach(match => {
      const { from, to, node } = match;
      const marks = state.doc.resolve(from).marks();
      
      // 只保留CHARACTER_NAME标记
      const characterNameMarks = marks.filter(mark => 
        mark.type.name === 'textType' && 
        mark.attrs.text_type === 'CHARACTER_NAME'
      );
      
      // 在事务中替换
      tr.replaceWith(
        from, 
        to, 
        state.schema.text(replaceText.value, characterNameMarks)
      );
      
      modified = true;
    });
    
    if (modified) {
      editor.view.dispatch(tr);
    }
  });
  
  // 重新计算匹配项
  setTimeout(() => {
    findAllMatches();
    if (currentIndex >= 0 && currentIndex < currentMatches.value.length) {
      currentMatchIndex.value = currentIndex;
    } else if (currentMatches.value.length > 0) {
      currentMatchIndex.value = 0;
    } else {
      currentMatchIndex.value = -1;
    }
  }, 100);
}

// 在watch中监听查找文本变化
watch(findText, () => {
  if (showFindReplaceDialog.value && findText.value) {
    findAllMatches();
  } else {
    matchCount.value = 0;
    currentMatchIndex.value = -1;
    currentMatches.value = [];
  }
});

onMounted(async () => {
  window.syncContent = async function (...args) {
    syncContent()
    refreshSceneHeaderList()
  }
  window.deleteSceneHeaderGlobal = deleteSceneHeaderGlobal
  window.computeGlobalPos = computeGlobalPos
  applyTextTypeStylesFromServer(styles)

  const html = preprocessHTML(`<div id="1947928043261493249" data-scene-header="true" data-episode="1" data-scene="9" data-scene-name="青岛喜马拉雅"
    data-mood="日" data-interior-exterior-scene="外" data-editing="false" style="height: 70px">
    <div style="display:none" text_type="ES_TITLE"><span text_type="ES_TITLE"
            style="font-size: 12.0pt;font-family: '宋体', SimSun, sans-serif;font-weight:bold;letter-spacing: 1pt;">1-9 日
            青岛喜马拉雅 外</span></p>
    </div>
</div>
<p style="text-align: left;"><span text_type="ACTION"
        style="font-size: 12.0pt;font-family: '宋体', SimSun, sans-serif;font-style:italic;text-decoration:underline;letter-spacing: 12pt;">余则成和吕宗方在椅子旁</span><span
        style="font-size: 12pt;font-family: 宋体;">边跳舞。</span></p>
<p style="text-align: left;"><span text_type="ACTION"
        style="font-size: 12.0pt;font-family: '宋体', SimSun, sans-serif;font-style:italic;text-decoration:underline;letter-spacing: 12pt;">要问跳的是什么舞，那必然</span><span
        style="font-size: 12pt;font-family: 宋体;">是霹雳舞。</span></p><span
    style="font-size: 12pt;font-family: 宋体;">这是一套贴地飞行的舞步。</span><span style="font-size: 12pt;font-family: 宋体;">。</span>
</p>
<p style="text-align: left;"><span text_type="CHARACTER_NAME"
        style="font-size: 18.0pt;font-family: '华文楷体', STKaiti, serif;font-weight:bold;font-style:italic;text-decoration:underline;letter-spacing: 12pt;">毛人</span><span
        style="font-size: 12pt;font-family: 宋体;">凤</span></p>
<p style="text-align: center;"><span text_type="DIALOGUE_DESCRIPTION"
        style="font-size: 12.0pt;font-family: '黑体', SimHei, sans-serif;letter-spacing: 20pt;">看了大</span></p>
<p style="text-align: left;"><span style="font-size: 12pt;font-family: 宋体;">惊</span><span text_type="DIALOGUE"
        style="font-size: 12.0pt;font-family: '宋体', SimSun, sans-serif;letter-spacing: 12pt;">道：没想到你这厮居然会这个步伐，滑板鞋在地板上摩擦的动作看起来甚是赏心悦目，难不成</span><span
        style="font-size: 12pt;font-family: 宋体;">你认识约瑟翰·庞麦郎?</span></p>
<div id="1947928043253104642" data-scene-header="true" data-episode="1" data-scene="8" data-scene-name="戴笠办公室"
    data-mood="日" data-interior-exterior-scene="内" data-editing="false" style="height: 70px">
    <div style="display:none" text_type="ES_TITLE"><span text_type="ES_TITLE"
            style="font-size: 12.0pt;font-family: '宋体', SimSun, sans-serif;font-weight:bold;letter-spacing: 1pt;">1-8 日
            戴笠办公室 内</span></p>
    </div>
</div>
<p style="text-align: left;"><span text_type="CHARACTER_NAME"
        style="font-size: 18.0pt;font-family: '华文楷体', STKaiti, serif;font-weight:bold;font-style:italic;text-decoration:underline;letter-spacing: 12pt;">乔峰</span><span
        style="font-size: 12pt;font-family: 宋体;">说</span><span text_type="DIALOGUE"
        style="font-size: 12.0pt;font-family: '宋体', SimSun, sans-serif;letter-spacing: 12pt;">：我是来测试覆盖场景</span><span
        style="font-size: 12pt;font-family: 宋体;">的</span><span text_type="CHARACTER_NAME"
        style="font-size: 18.0pt;font-family: '华文楷体', STKaiti, serif;font-weight:bold;font-style:italic;text-decoration:underline;letter-spacing: 12pt;">，段誉你小子</span><span
        style="font-size: 12pt;font-family: 宋体;">切</span><span
        style="font-size: 12pt;font-family: 宋体;">勿再点出六脉神剑。</span><span style="font-size: 12pt;font-family: 宋体;">。</span>
</p>
<p style="text-align: left;"><span text_type="CHARACTER_NAME"
        style="font-size: 18.0pt;font-family: '华文楷体', STKaiti, serif;font-weight:bold;font-style:italic;text-decoration:underline;letter-spacing: 12pt;">段誉</span><span
        style="font-size: 12pt;font-family: 宋体;">不</span></p>
<p style="text-align: center;"><span text_type="DIALOGUE_DESCRIPTION"
        style="font-size: 12.0pt;font-family: '黑体', SimHei, sans-serif;letter-spacing: 20pt;">知所措地</span></p>
<p style="text-align: left;"><span style="font-size: 12pt;font-family: 宋体;">解</span><span text_type="CHARACTER_NAME"
        style="font-size: 18.0pt;font-family: '华文楷体', STKaiti, serif;font-weight:bold;font-style:italic;text-decoration:underline;letter-spacing: 12pt;">释道：</span><span
        style="font-size: 12pt;font-family: 宋体;">大</span><span text_type="DIALOGUE"
        style="font-size: 12.0pt;font-family: '宋体', SimSun, sans-serif;letter-spacing: 12pt;">哥呀，这六脉神剑也不是我想点出就能点出，想不点出就能让它老老实实待着的东西，我这内力尚浅，要不你指点弟弟一二，听说还有一</span><span
        style="font-size: 12pt;font-family: 宋体;">招从天而降的掌法。</span></p>`)
  await initPages(html);
})
</script>


<style>
.multi-page-editor {
  width: 816px;
  margin: 20px auto;
  background: #f0f0f0;
  padding: 20px 0;
  user-select: text;
  -webkit-font-smoothing: antialiased;
  font-family: "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial,
    sans-serif;
}

.page {
  width: 794px;
  height: 1122px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1);
  margin: 24px auto;
  padding: 48px 72px 64px;
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  font-size: 14px;
  line-height: 1.7;
  color: #222;
  user-select: text;
}

.page-editor {
  flex: 1;
  overflow-y: auto;
  outline: none;
  user-select: text;
  /* 内容默认左对齐 */
  text-align: left;
}

/* 移除聚焦时的蓝色边框和阴影 */
.page-editor:focus,
.page-editor:focus-within,
.page-editor .ProseMirror:focus,
.page-editor .ProseMirror:focus-within {
  outline: none !important;
  box-shadow: none !important;
}

/* 底部分页页码居中 */
.page-number {
  position: absolute;
  bottom: 24px;
  left: 0;
  width: 100%;
  text-align: center;
  font-size: 13px;
  color: #999;
  font-weight: 500;
  user-select: none;
  pointer-events: none;
}

/* 查找替换弹窗样式 */
.find-replace-dialog {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 400px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 1000;
  font-family: 'Microsoft YaHei', sans-serif;
}

.dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid #eee;
}

.dialog-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}

.close-btn {
  background: none;
  border: none;
  font-size: 20px;
  cursor: pointer;
  color: #999;
}

.close-btn:hover {
  color: #333;
}

.dialog-body {
  padding: 16px;
}

.input-group {
  margin-bottom: 16px;
}

.input-group label {
  display: block;
  margin-bottom: 6px;
  font-size: 14px;
  color: #666;
}

.input-group input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 14px;
}

.input-group input:focus {
  outline: none;
  border-color: #409eff;
}

.match-info {
  margin: 12px 0;
  font-size: 14px;
  color: #666;
  text-align: center;
}

.dialog-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

.dialog-actions button {
  padding: 6px 12px;
  background: #f0f0f0;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.dialog-actions button:hover {
  background: #e0e0e0;
}

.dialog-actions button:last-child {
  background: #409eff;
  color: white;
  border-color: #409eff;
}

.dialog-actions button:last-child:hover {
  background: #66b1ff;
}

</style>